package com.jrx.anytxn.reporting.service.impl;

import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.uitl.DateUtils;
import com.jrx.anytxn.common.uitl.FileUtils;
import com.jrx.anytxn.common.uitl.StringUtils;
import com.jrx.anytxn.customer.constant.CustomerConstant;
import com.jrx.anytxn.customer.entity.CmCustomerAddressInfo;
import com.jrx.anytxn.customer.entity.CmCustomerContactInfo;
import com.jrx.anytxn.customer.entity.CmCustomerFirstLevel;
import com.jrx.anytxn.customer.entity.CmCustomerSecondLevel;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerAddressInfoMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerContactInfoMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerSecondLevelMapper;
import com.jrx.anytxn.customer.service.ICustomerService;
import com.jrx.anytxn.param.entity.*;
import com.jrx.anytxn.param.mapper.ext.ExtBtBatchStatusMapper;
import com.jrx.anytxn.param.mapper.ext.ExtPrChannelTableMapper;
import com.jrx.anytxn.param.service.ntfs.ICreateTskdFileSampleSimple;
import com.jrx.anytxn.param.service.product.IProductTableService;
import com.jrx.anytxn.param.service.system.ICodeTableService;
import com.jrx.anytxn.param.service.system.IOrganizationTableService;
import com.jrx.anytxn.reporting.constant.ReportingConstant;
import com.jrx.anytxn.reporting.service.ILSFileService;
import com.jrx.anytxn.transaction.constant.TransBizConstant;
import com.jrx.anytxn.transaction.entity.*;
import com.jrx.anytxn.transaction.mapper.ext.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 临商文件处理实现类
 *
 * @author liuyang
 * @date 2021-03-13
 */
@Service
public class LSFileServiceImpl implements ILSFileService {

    private static final Logger logger = LoggerFactory.getLogger(LSFileServiceImpl.class);

    @Resource
    private ExtTlPaymentAllocationLogMapper extTlPaymentAllocationLogMapper;

    @Resource
    private ExtAmLoanMainInfoMapper extAmLoanMainInfoMapper;

    @Resource
    private ExtAmLoanAccountInfoMapper extAmLoanAccountInfoMapper;

    @Resource
    private ExtAmRepaymentPlanInfoMapper extAmRepaymentPlanInfoMapper;

    @Resource
    private ExtAmAccountMainInfoMapper extAmAccountMainInfoMapper;

    @Resource
    private ExtAmInterestAccrualInfoMapper extAmInterestAccrualInfoMapper;

    @Resource
    private ExtCmCustomerAddressInfoMapper extCmCustomerAddressInfoMapper;

    @Resource
    private ExtCmCustomerContactInfoMapper extCmCustomerContactInfoMapper;

    @Autowired
    private ICustomerService customerService;

    @Autowired
    private IProductTableService productTableService;

    @Autowired
    private IOrganizationTableService organizationTableService;

    @Autowired
    private ICodeTableService codeTableService;

    @Autowired
    private ICreateTskdFileSampleSimple createTskdFileSampleSimple;

    @Resource
    private ExtBtBatchStatusMapper extBtBatchStatusMapper;

    @Resource
    private ExtCmCustomerSecondLevelMapper extCmCustomerSecondLevelMapper;

    @Resource
    private ExtPrChannelTableMapper extPrChannelTableMapper;

    private static final Map<String, Map> codeMap;

    static {
        codeMap = new HashMap<>();

        // 业务种类细分
        Map<String, String> loanTypeMap = new HashMap() {{
            put("1", "91");//1 现金贷 - 91 个人消费贷款
            put("2", "91");//2 消费分期 - 91 个人消费贷款
            put("3", "99");//3 账单分期 - 99-其他
            put("4", "99");//4 信用付 - 99-其他
            put("5", "91");//5 小额现金贷 - 91 个人消费贷款
            put("6", "41");//6 经营贷 - 41 个人经营性贷款
        }};
        codeMap.put("LOAN_TYPE", loanTypeMap);

        // 五级分类
        Map<String, String> loanClassifyMap = new HashMap() {{
            put("0", "1");//0 正常 - 1 正常
            put("1", "2");//1 关注 - 2 关注
            put("2", "3");//2 次级 - 3 次级
            put("3", "4");//3 可疑 - 4 可疑
            put("4", "5");//4 损失 - 5 损失
        }};
        codeMap.put("LOAN_CLASSIFY", loanClassifyMap);

        // 账户状态
        Map<String, String> accountStatusMap = new HashMap() {{
            put("0", "1");//0 新建  - 1 正常
            put("1", "1");//1 正常 - 1 正常
            put("2", "2");//2 逾期 - 2 逾期
            put("7", "3");//7 提前结清 - 3 结清
            put("8", "3");//8 正常结清  - 3 结清
            put("9", "1");//9 取消  - 1 正常
        }};
        codeMap.put("ACCOUNT_STATUS", accountStatusMap);

        // 证件类型
        Map<String, String> idTypeMap = new HashMap() {{
            put("B01", "0");//B01 身份证 - 0 身份证
        }};
        codeMap.put("ID_TYPE", idTypeMap);

        // 学历
        Map<String, String> educationMap = new HashMap() {{
            put("X", "99");//X 未知 - 99 未知
            put("3", "20");//3 本科 - 20 大学本科
            put("2", "10");//2 研究生 - 10 研究生
            put("7", "40");//7 技术学校 - 40 中等专业学校或中等技术学校
            put("6", "40");//6 中专 - 40 中等专业学校或中等技术学校
            put("5", "60");//5 高中 - 60 高中
            put("4", "30");//4 专科 - 30 大学专科和专科学校
            put("1", "10");//1 博士 - 10 研究生 （征信报送学历没有博士）
            put("8", "70");//8 初中及以下 - 70 初中
            put("999999", "99");//默认 - 99 未知
        }};
        codeMap.put("EDUCATION", educationMap);

        // 婚姻状况
        Map<String, String> maritalStatusMap = new HashMap() {{
            put("3", "40");//3 离异 - 40 离婚
            put("1", "10");//1 未婚 - 10 未婚
            put("4", "30");//4 丧偶 - 30 丧偶
            put("Z", "90");//Z 其他 - 90 未说明的婚姻状况
            put("2", "20");//2 已婚 - 20 已婚
            put("999999", "90");//默认 - 90 未说明的婚姻状况
        }};
        codeMap.put("MARITAL_STATUS", maritalStatusMap);

        // 职务
        Map<String, String> jobCodeMap = new HashMap() {{
            put("8", "9");//8 其他 - 9 未知
            put("7", "3");//7 一般员工 - 3 一般员工
            put("5", "2");//5 中层管理人员 - 2 中级领导（行政级别局级以下领导或大公司中级管理人员）
            put("2", "2");//2 科级 - 2 中级领导（行政级别局级以下领导或大公司中级管理人员）
            put("1", "2");//1 处级 - 2 中级领导（行政级别局级以下领导或大公司中级管理人员）
            put("6", "2");//6 一般管理人员 - 2 中级领导（行政级别局级以下领导或大公司中级管理人员）
            put("3", "2");//3 一般干部 - 2 中级领导（行政级别局级以下领导或大公司中级管理人员）
            put("4", "1");//4 企业负责人 - 1 高级领导（行政级别局级及局级以上领导或大公司高级管理人员）
            put("0", "1");//0 厅局级以上 - 1 高级领导（行政级别局级及局级以上领导或大公司高级管理人员）
            put("999999", "9");//默认 9 未知

        }};
        codeMap.put("JOB_CODE", jobCodeMap);

        // 居住情况
        Map<String, String> familyStatusMap = new HashMap() {{
            put("113", "2");//113 组合贷款 - 2 按揭
            put("330", "3");//330 亲戚/父母家 - 3 亲属楼宇
            put("320", "4");//320 集体宿舍 - 4 集体宿舍
            put("112", "2");//112 商业贷款购房 - 2 按揭
            put("120", "1");//120 商品房(无按揭) - 1 自置
            put("111", "2");//111 公积金贷款购房 - 2 按揭
            put("200", "1");//200 自有无产权房 - 1 自置
            put("310", "5");//310 租房 - 5 租房
            put("900", "7");//900 其它 - 7 其他
            put("130", "1");//130 已购公房 - 1 自置
            put("999999", "9");//默认 9 未知
        }};
        codeMap.put("FAMILY_STATUS", familyStatusMap);

        // 职业
        Map<String, String> occuptionTypeMap = new HashMap() {{
            put("4", "4");// 4 商业服务 从业人员 - 4 商业、服务业人员
            put("8", "Y");// 8 自雇人士 - Y 不便分类的其他从业人员
            put("X", "Z");// X 未知 - Z 未知
            put("6", "6");// 6 生产运输 从业人员 - 6 生产、运输设备操作人员及有关人员
            put("3", "3");// 3 政府/企事业单位 职员 - 3 办事人员和有关人员
            put("5", "5");// 5 农林牧渔水利 从业人员 - 5 农、林、牧、渔、水利业生产人员
            put("2", "1");// 2 专业技术人员 - 1 专业技术人员
            put("1", "0");// 1 政府/企事业单位 负责人 - 0 国家机关、党群组织、企业、事业单位负责人
            put("7", "X");// 7 军人/警察 - X 军人
            put("999999", "Z");//默认 Z 未知
        }};
        codeMap.put("OCCUPTION_TYPE", occuptionTypeMap);

        // 还款方式
        Map<String, String> chargeOptionMap = new HashMap() {{
            put("01", "19");// 01 分期付款 - 19 其他类型分期还款
            put("02", "11");// 02 等额本息 - 11 分期等额本息
            put("03", "12");// 03 等额本金 - 12 分期等额本金
            put("06", "13");// 06 按月付息到期还本 - 13 到期还本分期结息
            put("08", "19");// 08 按月付息按年还本 - 19 其他类型分期还款
            put("09", "23");// 09 利随本清 - 23 随时还
        }};
        codeMap.put("CHARGEOPTION", chargeOptionMap);

        // 单位性质
        Map<String, String> companyAttributeMap = new HashMap() {{
            put("0", "20");// 0 国有企业 - 20 国有企业
            put("6", "10");// 6 国家机关 - 10 机关、事业单位
            put("4", "40");// 4 私营企业（民营企业） - 40 个体、私营企业
            put("7", "50");// 7 其它 - 50 其他（包括三资企业、民营企业、民间团体等）
            put("1", "20");// 1 国有控股企业 - 20 国有企业
            put("2", "30");// 2 外资企业 - 30 外资企业
            put("5", "10");// 5 事业单位 - 10 机关、事业单位
            put("3", "50");// 3 合资企业 - 50 其他（包括三资企业、民营企业、民间团体等）
            put("8", "40");// 8 个体工商户 - 40 个体、私营企业
            put("999999", "50");//默认 50 其他
        }};
        codeMap.put("COMPANY_ATTRIBUTE", companyAttributeMap);

        // 关系类型
        Map<String, String> relationMap = new HashMap() {{
            put("00", "5");// 00 父母 - 5 父母
            put("01", "1");// 01 配偶 - 1 配偶
            put("02", "2");// 02 子女 - 2 子 3 女（征信系统，子，女是分开，这里映射为子）
            put("03", "7");// 03 兄弟 - 7 兄、弟、姐、妹
            put("04", "7");// 04 姐妹 - 7 兄、弟、姐、妹
            put("05", "8");// 05 同学 - 8 其他
            put("06", "8");// 06 亲属 - 8 其他
            put("07", "8");// 07 朋友 - 8 其他
            put("08", "8");// 08 同事 - 8 其他
            put("09", "8");// 09 其它 - 8 其他
        }};
        codeMap.put("RELATION", relationMap);
    }

    /**
     * 根据文件名生成对应目录和空文件
     *
     * @param basePath
     * @param fileName
     * @param fileType
     * @param fileSuffix
     * @author liuyang
     * @date 2021-03-13
     */
    @Override
    public void createDirectoriesAndFile(String basePath, String fileName, String fileType, String fileSuffix) {

        // 查询机构信息
        PrOrganizationTable org = organizationTableService.findByOrgIdForBatch(ReportingConstant.DEFAULT_ORG_NUMBER);
        String toDay = DateUtils.format(org.getToday(), DateUtils.DATE_PATTERN_ANOTHER);

        // 文件名
        StringBuilder generateFileNameSb = new StringBuilder();
        generateFileNameSb.append(fileType).append(toDay).append("_").append(fileName).append(fileSuffix);
        String generateFileName = generateFileNameSb.toString();

        // 文件目录
        StringBuilder dirPathSb = new StringBuilder();
        dirPathSb.append(basePath).append(File.separator).append(org.getTenantId()).append(File.separator).append(toDay);
        String dirPath = dirPathSb.toString();

        // 文件全路径
        StringBuilder filePathSb = new StringBuilder();
        filePathSb.append(dirPathSb).append(File.separator).append(generateFileName);
        String filePath = filePathSb.toString();

        try {
            // 创建目录
            FileUtils.createDirectories(dirPath);
            // 判断文件是否存，如果存在，则删除重新创建，支持重跑机制
            if (FileUtils.isExists(filePath)) {
                FileUtils.deleteFile(filePath);
            }
            // 创建文件
            FileUtils.createFile(filePath);
        } catch (TxnException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 生成账户信息
     *
     * @param loanId
     * @return String
     * @author liuyang
     * @date 2021-03-13
     */
    @Override
    public String createLSAccountFileMessage(String loanId) throws TxnBizException {

        logger.debug("临商账户信息文件生成，订单账户信息[{}]", loanId);

        AmLoanAccountInfo amLoanAccountInfo = extAmLoanAccountInfoMapper.selectByLoanIdForBatch(loanId);
        AmLoanMainInfo amLoanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndTenantId(amLoanAccountInfo.getLoanId(),
                amLoanAccountInfo.getTenantId());

        // 逾期余额计算
        BigDecimal overdueBalance = BigDecimal.ZERO;
        List<AmAccountMainInfo> amAccountMainInfos = extAmAccountMainInfoMapper.selectAccounts(amLoanMainInfo.getLoanId(),
                Arrays.asList(TransBizConstant.ACCOUNT_TYPE_06), amLoanMainInfo.getCustomerId(), amLoanMainInfo.getTenantId(),
                Arrays.asList(TransBizConstant.ACCOUNT_STATUS_2));
        if (!CollectionUtils.isEmpty(amAccountMainInfos)) {
            overdueBalance = overdueBalance.add(amAccountMainInfos.stream().map(AmAccountMainInfo::getCurrBalance).reduce(BigDecimal.ZERO, BigDecimal::add));
        }

        // 欠息余额计算 截止到目前所有的欠息余额(未还的利息+未还的罚息+未还的复利+每日累计罚息+每日累计的复利)，未到期每日产生的利息不计。
        BigDecimal interest = BigDecimal.ZERO;
        interest = interest.add(amLoanAccountInfo.getInterestBalance()).add(amLoanAccountInfo.getPenaltyBalance())
                .add(amLoanAccountInfo.getCompoundBalance()).add(amLoanAccountInfo.getAccruedPenalty()).add(amLoanAccountInfo.getAccruedCompound());

        // 最早逾期日期
        String overdueDateStr = "";
        if (TransBizConstant.LOAN_STATUS_2.equals(amLoanAccountInfo.getAccountStatus())) {
            List<AmRepaymentPlanInfo> amRepaymentPlanInfos =
                    extAmRepaymentPlanInfoMapper.selectByLoanIdAndStatus(amLoanMainInfo.getLoanId(),
                            Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_2), amLoanMainInfo.getCustomerId(),
                            amLoanMainInfo.getTenantId());
            if (!CollectionUtils.isEmpty(amRepaymentPlanInfos)) {
                AmRepaymentPlanInfo amRepaymentPlanInfo =
                        amRepaymentPlanInfos.stream().min(Comparator.comparing(AmRepaymentPlanInfo::getBillingTenor)).get();
                overdueDateStr = DateUtils.format(amRepaymentPlanInfo.getPaymentDueDate(), DateUtils.DATE_PATTERN_LINE);
            }
        }

        // 宽限期计算
        PrProductTable productTable = productTableService.findProByProIdAndOrgId(amLoanMainInfo.getProductId(),
                amLoanMainInfo.getOrganizationId(), amLoanMainInfo.getTenantId());
        Integer graceDays = productTable.getGraceDays();
        //按订单计算 订单级宽限期，若往期处于逾期状态，则当期没有宽限期
        if (TransBizConstant.GRACE_OPTION_BY_LOAN.equals(productTable.getGraceOption()) &&
                TransBizConstant.LOAN_STATUS_2.equals(amLoanAccountInfo.getAccountStatus())) {
            graceDays = 0;
        }
        String graceDateStr = "";
        if (graceDays != null && graceDays > 0) {
            graceDateStr = DateUtils.format(DateUtils.addDay(amLoanAccountInfo.getNextPaymentDate(), graceDays),
                    DateUtils.DATE_PATTERN_LINE);
        }

        // 结清日期
        String payoffDateStr = "";
        if (amLoanAccountInfo.getPayoffDate() != null) {
            payoffDateStr = DateUtils.format(amLoanAccountInfo.getPayoffDate(), DateUtils.DATE_PATTERN_LINE);
        }

        // 应还日期
        String payDateStr = "";
        if (amLoanAccountInfo.getNextPaymentDate() != null) {
            payDateStr = DateUtils.format(amLoanAccountInfo.getNextPaymentDate(), DateUtils.DATE_PATTERN_LINE);
        }

        // 获取客户一级信息
        CmCustomerFirstLevel cmCustomerFirstLevel =
                customerService.getFirstLevelByCondition(amLoanMainInfo.getCustomerId(),
                        amLoanMainInfo.getOrganizationId(), amLoanMainInfo.getTenantId());

        // 获取机构信息
        PrOrganizationTable organizationTable =
                organizationTableService.findByOrId(amLoanMainInfo.getOrganizationId(),
                        amLoanMainInfo.getTenantId());

        StringBuilder sb = new StringBuilder();
        sb.append(ReportingConstant.FIELD_PRE_LS).append(amLoanMainInfo.getLoanId()).append(ReportingConstant.SEPARATOR_LS);//贷款业务号
        sb.append(ReportingConstant.FIELD_PRE_LS).append(amLoanMainInfo.getContractId()).append(ReportingConstant.SEPARATOR_LS);//贷款合同号
        sb.append(ReportingConstant.LS_ZX_ORGID).append(ReportingConstant.SEPARATOR_LS);//金融机构
        sb.append(this.getICRCode("LOAN_TYPE", amLoanMainInfo.getLoanType())).append(ReportingConstant.SEPARATOR_LS);//业务种类细分
        sb.append("03").append(ReportingConstant.SEPARATOR_LS);//还款频率 03-月
        sb.append("CNY").append(ReportingConstant.SEPARATOR_LS);//币种
        sb.append(amLoanMainInfo.getLoanAmount()).append(ReportingConstant.SEPARATOR_LS);//合同金额
        sb.append(amLoanMainInfo.getLoanAmount()).append(ReportingConstant.SEPARATOR_LS);//本金总额=借据的发放总金额
        sb.append(amLoanAccountInfo.getRemainPrincipal() != null ? amLoanAccountInfo.getRemainPrincipal().subtract(overdueBalance) : BigDecimal.ZERO).append(ReportingConstant.SEPARATOR_LS);//正常余额（贷款剩余本金-逾期本金）
        sb.append(overdueBalance).append(ReportingConstant.SEPARATOR_LS);//逾期余额=逾期本金
        sb.append(interest).append(ReportingConstant.SEPARATOR_LS);//欠息余额
        sb.append(DateUtils.format(amLoanMainInfo.getPostingDate(), DateUtils.DATE_PATTERN_LINE)).append(ReportingConstant.SEPARATOR_LS);//发放日期
        sb.append(amLoanMainInfo.getLastPaymentDate()==null ? "" : DateUtils.format(amLoanMainInfo.getLastPaymentDate(), DateUtils.DATE_PATTERN_LINE)).append(ReportingConstant.SEPARATOR_LS);//到期日期
        sb.append(9).append(ReportingConstant.SEPARATOR_LS);//担保方式 9-其他
        sb.append(StringUtils.isNotBlank(amLoanAccountInfo.getLoanClassify()) ? this.getICRCode("LOAN_CLASSIFY",
                amLoanAccountInfo.getLoanClassify()) : "1").append(ReportingConstant.SEPARATOR_LS);//五级分类
        sb.append(this.getICRCode("ACCOUNT_STATUS", amLoanAccountInfo.getAccountStatus())).append(ReportingConstant.SEPARATOR_LS);//帐户状态
        sb.append(payoffDateStr).append(ReportingConstant.SEPARATOR_LS);//结清日期
        sb.append("").append(ReportingConstant.SEPARATOR_LS);//担保信息摘要
        sb.append(overdueDateStr).append(ReportingConstant.SEPARATOR_LS);//最早逾期日期
        sb.append(payDateStr).append(ReportingConstant.SEPARATOR_LS);//应还日期
        sb.append(graceDateStr).append(ReportingConstant.SEPARATOR_LS);//宽限期最后一天
        sb.append("").append(ReportingConstant.SEPARATOR_LS);// 上报标志
        sb.append(ReportingConstant.FIELD_PRE_LS).append(amLoanMainInfo.getCustomerId()).append(ReportingConstant.SEPARATOR_LS);//客户编号
        sb.append(cmCustomerFirstLevel.getChineseName()).append(ReportingConstant.SEPARATOR_LS);//客户名称
        sb.append(this.getICRCode("ID_TYPE", cmCustomerFirstLevel.getIdType())).append(ReportingConstant.SEPARATOR_LS);//证件类型
        sb.append(cmCustomerFirstLevel.getIdNumber()).append(ReportingConstant.SEPARATOR_LS);//证件号码
        sb.append(DateUtils.format(organizationTable.getToday(), DateUtils.DATE_PATTERN_LINE)).append(ReportingConstant.SEPARATOR_LS);//发生日期
        sb.append("").append(ReportingConstant.SEPARATOR_LS);// 管护人
        sb.append("");// 管护机构

        return sb.toString();
    }

    /**
     * 生成还款流水
     *
     * @param tlThirdPartyPayment
     * @return String
     * @author liuyang
     * @date 2021-03-13
     */
    @Override
    public String createLSThirdPaymentFileMessage(TlThirdPartyPayment tlThirdPartyPayment) {

        logger.debug("临商还款流水文件生成，还款流水[{}]", tlThirdPartyPayment.getSeqNo());

        // 实还本金，实还利息计算
        BigDecimal principalRepayAmt = BigDecimal.ZERO;
        BigDecimal interestRepayAmt = BigDecimal.ZERO;
        List<TlPaymentAllocationLog> paymentAllocationLogs =
                extTlPaymentAllocationLogMapper.findCustLog(tlThirdPartyPayment.getLoanId(),
                        tlThirdPartyPayment.getSeqNo(), tlThirdPartyPayment.getCustomerId(),
                        tlThirdPartyPayment.getTenantId());
        if (!CollectionUtils.isEmpty(paymentAllocationLogs)) {
            principalRepayAmt = paymentAllocationLogs.stream().filter(log -> TransBizConstant.ACCOUNT_TYPE_06.equals(log.getConnectAccountType()))
                    .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            interestRepayAmt = paymentAllocationLogs.stream().filter(log -> Arrays.asList(TransBizConstant.ACCOUNT_TYPE_04,
                    TransBizConstant.ACCOUNT_TYPE_11, TransBizConstant.ACCOUNT_TYPE_12).contains(log.getConnectAccountType()))
                    .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        StringBuilder sb = new StringBuilder();
        sb.append(ReportingConstant.FIELD_PRE_LS).append(tlThirdPartyPayment.getLoanId()).append(ReportingConstant.SEPARATOR_LS);//贷款业务号
        sb.append(tlThirdPartyPayment.getSeqNo()).append(ReportingConstant.SEPARATOR_LS);//还款流水号
        sb.append(DateUtils.format(tlThirdPartyPayment.getBusinessDate(), DateUtils.DATE_PATTERN_LINE)).append(ReportingConstant.SEPARATOR_LS);
        //发生日期
        sb.append(principalRepayAmt).append(ReportingConstant.SEPARATOR_LS);//实还本金
        sb.append(interestRepayAmt).append(ReportingConstant.SEPARATOR_LS);;//实还利息
        sb.append("").append(ReportingConstant.SEPARATOR_LS);// 管护人
        sb.append("");// 管护机构

        return sb.toString();
    }

    /**
     * 生成个人信息
     *
     * @param cmCustomerFirstLevel
     * @return String
     * @author liuyang
     * @date 2021-03-13
     */
    @Override
    public String createLSCustFileMessage(CmCustomerFirstLevel cmCustomerFirstLevel) throws TxnBizException {

        logger.debug("临商个人信息文件生成，个人信息[{}]", cmCustomerFirstLevel.getCustomerId());

        // 根据日切结束时间获取最新的客户二级信息
        PrOrganizationTable org = organizationTableService.findByOrgIdForBatch(cmCustomerFirstLevel.getOrganizationId());
        BtBatchStatus statuses = extBtBatchStatusMapper.selectByStatusAndJobName(ReportingConstant.BATCH_SATTUS_2, ReportingConstant.CUTOVER_END,org.getTenantId(),org.getToday());
        Date updateTime = null;
        if(null != statuses){
            updateTime = statuses.getCreateTime();
        }

        // 查询需要征信报送的渠道id
        List<String> channelIds = extPrChannelTableMapper.selectByCreditReporting(cmCustomerFirstLevel.getTenantId(), ReportingConstant.YES_FLAG);

        // 多渠道客户，获取最新的客户二级信息
        CmCustomerSecondLevel cmCustomerSecondLevel = extCmCustomerSecondLevelMapper.selectByUpdateTimeAndCustId(cmCustomerFirstLevel.getTenantId(),cmCustomerFirstLevel.getCustomerId(), updateTime, channelIds);
        if (cmCustomerSecondLevel == null) {
            return null;
        }

        //获取联系人信息
        List<CmCustomerContactInfo> contactInfos = extCmCustomerContactInfoMapper.selectByCustomerIdAndUpdateTime(cmCustomerSecondLevel.getTenantId(), cmCustomerSecondLevel.getCustomerId(),
                null, channelIds, Collections.singletonList(CustomerConstant.CONTACT_TYPE_01));
        CmCustomerContactInfo customerContactInfo = null;
        String partnerName = "";//配偶姓名
        String partnerIdType = "";//配偶证件类型
        String partnerIdNo = "";//配偶证件号码
        String partnerCompanyName = "";//配偶工作单位
        String partnerMobile = "";//配偶联系电话
        if (!CollectionUtils.isEmpty(contactInfos)) {
            Optional<CmCustomerContactInfo> customerContactInfoOp = contactInfos.stream().filter(e -> ReportingConstant.YES_FLAG.equals(e.getContactStatus())).findFirst();
            if (customerContactInfoOp.isPresent()) {
                customerContactInfo = customerContactInfoOp.get();
                partnerName = customerContactInfo.getChineseName();
                partnerIdType = StringUtils.isNotBlank(customerContactInfo.getIdType()) ? customerContactInfo.getIdType() : "";
                partnerIdNo = StringUtils.isNotBlank(customerContactInfo.getIdNumber()) ? customerContactInfo.getIdNumber() : "";
                partnerCompanyName = StringUtils.filterSpecialChar(customerContactInfo.getCompanyName());
                partnerCompanyName = getLSLenStr(partnerCompanyName, 60);
                partnerMobile = StringUtils.isNotBlank(customerContactInfo.getMobile()) ? customerContactInfo.getMobile() : "";
            }
        }

        //获取客户地址信息
        List<CmCustomerAddressInfo> addressInfos = extCmCustomerAddressInfoMapper.selectByCustomerIdAndUpdateTime(cmCustomerSecondLevel.getTenantId(), cmCustomerSecondLevel.getCustomerId(),
                null, channelIds, null);

        String liveAddr = "未知"; // 居住地址
        String liveZipCode = "999999"; // 居住地址邮政编码
        String registerAddr = "未知"; // 户籍地址
        String companyAddr = "未知"; // 单位地址
        String companyZipCode = "999999"; // 单位地址邮政编码
        if (!CollectionUtils.isEmpty(addressInfos)) {
            Optional<CmCustomerAddressInfo> customerAddressInfo =
                    addressInfos.stream().filter(e -> CustomerConstant.ADDRESS_TYPE_1.equals(e.getAddressType())).findFirst();
            if (customerAddressInfo.isPresent()) {
                // 居住地址
                liveAddr = this.getAllAddress(customerAddressInfo.get());
                liveAddr = StringUtils.filterSpecialChar(liveAddr);
                liveAddr = getLSLenStr(liveAddr, 60); // 地址长度不能超过60
                // 居住地址邮政编码
                liveZipCode = StringUtils.isNotBlank(customerAddressInfo.get().getZipcode()) ? customerAddressInfo.get().getZipcode() : "999999";
            }

            // 户籍地址
            customerAddressInfo = addressInfos.stream().filter(e -> CustomerConstant.ADDRESS_TYPE_0.equals(e.getAddressType())).findFirst();
            if (customerAddressInfo.isPresent()) {
                registerAddr = this.getAllAddress(customerAddressInfo.get());
                registerAddr = StringUtils.filterSpecialChar(registerAddr);
                registerAddr = getLSLenStr(registerAddr, 60);
            }

            customerAddressInfo = addressInfos.stream().filter(e -> CustomerConstant.ADDRESS_TYPE_3.equals(e.getAddressType())).findFirst();
            if (customerAddressInfo.isPresent()) {
                // 单位地址
                companyAddr = this.getAllAddress(customerAddressInfo.get());
                companyAddr = StringUtils.filterSpecialChar(companyAddr);
                companyAddr = getLSLenStr(companyAddr, 60);
                // 单位地址邮政编码
                companyZipCode = StringUtils.isNotBlank(customerAddressInfo.get().getZipcode()) ? customerAddressInfo.get().getZipcode() : "999999";
            }
        }

        StringBuilder sb = new StringBuilder();
        sb.append(ReportingConstant.FIELD_PRE_LS).append(cmCustomerFirstLevel.getCustomerId()).append(ReportingConstant.SEPARATOR_LS);//客户编号
        sb.append(cmCustomerFirstLevel.getChineseName()).append(ReportingConstant.SEPARATOR_LS);//姓名
        sb.append(this.getICRCode("ID_TYPE", cmCustomerFirstLevel.getIdType())).append(ReportingConstant.SEPARATOR_LS);//证件类型
        sb.append(cmCustomerFirstLevel.getIdNumber()).append(ReportingConstant.SEPARATOR_LS);//证件号码
        sb.append(StringUtils.isNotBlank(cmCustomerFirstLevel.getSex()) ? cmCustomerFirstLevel.getSex() : "0").append(ReportingConstant.SEPARATOR_LS);//性别 0-未知 1-男 2-女
        sb.append(cmCustomerFirstLevel.getBirthDate()==null ? "" : DateUtils.format(cmCustomerFirstLevel.getBirthDate(), DateUtils.DATE_PATTERN_ANOTHER)).append(ReportingConstant.SEPARATOR_LS);
        //出生日期
        sb.append(StringUtils.isNotBlank(cmCustomerSecondLevel.getMaritalStatus()) ? this.getICRCode("MARITAL_STATUS",
                cmCustomerSecondLevel.getMaritalStatus()) : "90").append(ReportingConstant.SEPARATOR_LS);// 婚姻状况
        sb.append(StringUtils.isNotBlank(cmCustomerSecondLevel.getEducation()) ?
                this.getICRCode("EDUCATION", cmCustomerSecondLevel.getEducation()) : "99").append(ReportingConstant.SEPARATOR_LS);//最高学历
        sb.append(9).append(ReportingConstant.SEPARATOR_LS);//最高学位
        sb.append(StringUtils.isNotBlank(cmCustomerSecondLevel.getHomePhone()) ? cmCustomerSecondLevel.getHomePhone() : "").append(ReportingConstant.SEPARATOR_LS);//住宅电话
        sb.append(cmCustomerSecondLevel.getMobile()).append(ReportingConstant.SEPARATOR_LS);//手机号码
        sb.append(StringUtils.isNotBlank(cmCustomerSecondLevel.getCompanyPhone()) ? cmCustomerSecondLevel.getCompanyPhone() : "").append(ReportingConstant.SEPARATOR_LS);//单位电话
        sb.append("").append(ReportingConstant.SEPARATOR_LS);//电子邮箱
        sb.append(liveAddr).append(ReportingConstant.SEPARATOR_LS);//通讯地址
        sb.append(liveZipCode).append(ReportingConstant.SEPARATOR_LS);//通讯地址邮政编码
        sb.append(registerAddr).append(ReportingConstant.SEPARATOR_LS);//户籍地址
        sb.append(partnerName).append(ReportingConstant.SEPARATOR_LS);//配偶姓名
        sb.append(StringUtils.isNotBlank(partnerIdType) ? this.getICRCode("ID_TYPE", partnerIdType) : "").append(ReportingConstant.SEPARATOR_LS);//配偶证件类型
        sb.append(partnerIdNo).append(ReportingConstant.SEPARATOR_LS);//配偶证件号码
        sb.append(partnerCompanyName).append(ReportingConstant.SEPARATOR_LS);//配偶工作单位
        sb.append(partnerMobile).append(ReportingConstant.SEPARATOR_LS);//配偶联系电话
        sb.append(StringUtils.isNotBlank(cmCustomerSecondLevel.getOccuptionType()) ? this.getICRCode("OCCUPTION_TYPE", cmCustomerSecondLevel.getOccuptionType()) : "Z").append(ReportingConstant.SEPARATOR_LS);//职业
        sb.append(StringUtils.isNotBlank(cmCustomerSecondLevel.getCompanyName()) ? getLSLenStr(StringUtils.filterSpecialChar(cmCustomerSecondLevel.getCompanyName()), 60) : "未知").append(ReportingConstant.SEPARATOR_LS);//单位名称
        sb.append(StringUtils.isNotBlank(cmCustomerSecondLevel.getIndustry()) ? cmCustomerSecondLevel.getIndustry().substring(0, 1) : "Z").append(ReportingConstant.SEPARATOR_LS);//单位所属行业
        sb.append(companyAddr).append(ReportingConstant.SEPARATOR_LS);//单位地址
        sb.append(companyZipCode).append(ReportingConstant.SEPARATOR_LS);//单位地址邮政编码
        sb.append("").append(ReportingConstant.SEPARATOR_LS);//本单位工作起始年份
        sb.append(StringUtils.isNotBlank(cmCustomerSecondLevel.getPosition()) ? this.getICRCode("JOB_CODE", cmCustomerSecondLevel.getPosition()) :
                "9").append(ReportingConstant.SEPARATOR_LS);//职务
        sb.append(9).append(ReportingConstant.SEPARATOR_LS);//职称
        sb.append("").append(ReportingConstant.SEPARATOR_LS);//年收入
        sb.append("").append(ReportingConstant.SEPARATOR_LS);//工资帐号
        sb.append("").append(ReportingConstant.SEPARATOR_LS);//工资账户开户银行
        sb.append(liveAddr).append(ReportingConstant.SEPARATOR_LS);//居住地址
        sb.append(liveZipCode).append(ReportingConstant.SEPARATOR_LS);//居住地址邮政编码
        sb.append(StringUtils.isNotBlank(cmCustomerSecondLevel.getFamilyStatus()) ? this.getICRCode("FAMILY_STATUS",
                cmCustomerSecondLevel.getFamilyStatus()) : "9").append(ReportingConstant.SEPARATOR_LS);//居住状况
        sb.append("").append(ReportingConstant.SEPARATOR_LS);// 管护人
        sb.append("");// 管护机构

        return sb.toString();
    }

    /**
     * 生成还款计划
     *
     * @param planId
     * @return String
     * @author liuyang
     * @date 2021-03-13
     */
    @Override
    public String createLSRepaymentFileMessage(Long planId) throws TxnBizException {

        logger.debug("临商还款计划文件生成，还款计划[{}]", planId);

        AmRepaymentPlanInfo amRepaymentPlanInfo = extAmRepaymentPlanInfoMapper.selectByPrimaryKey(planId);

        // 获取机构信息
        PrOrganizationTable organizationTable =
                organizationTableService.findByOrId(amRepaymentPlanInfo.getOrganizationId(),
                        amRepaymentPlanInfo.getTenantId());

        // 宽限期计算
        AmLoanAccountInfo amLoanAccountInfo =
                extAmLoanAccountInfoMapper.selectByLoanIdForBatch(amRepaymentPlanInfo.getLoanId());
        PrProductTable productTable = productTableService.findProByProIdAndOrgId(amRepaymentPlanInfo.getProductId(),
                amRepaymentPlanInfo.getOrganizationId(), amRepaymentPlanInfo.getTenantId());
        Integer graceDays = productTable.getGraceDays();
        //按订单计算 订单级宽限期，若往期处于逾期状态，则当期没有宽限期
        if (TransBizConstant.GRACE_OPTION_BY_LOAN.equals(productTable.getGraceOption()) &&
                TransBizConstant.LOAN_STATUS_2.equals(amLoanAccountInfo.getAccountStatus())) {
            graceDays = 0;
        }
        String graceDateStr = "";
        if (graceDays != null && graceDays > 0) {
            graceDateStr = DateUtils.format(DateUtils.addDay(amRepaymentPlanInfo.getPaymentDueDate(), graceDays),
                    DateUtils.DATE_PATTERN_LINE);
        }

        // 应还本金罚息计算, 应还利息罚息计算
        BigDecimal penaltyAmt = BigDecimal.ZERO;
        BigDecimal compoundAmt = BigDecimal.ZERO;
        List<AmAccountMainInfo> amAccountMainInfos =
                extAmAccountMainInfoMapper.selectAccount(amRepaymentPlanInfo.getLoanId(),
                        amRepaymentPlanInfo.getCustomerId(), amRepaymentPlanInfo.getTenantId(),
                        Arrays.asList(TransBizConstant.ACCOUNT_TYPE_11, TransBizConstant.ACCOUNT_TYPE_12),
                        amRepaymentPlanInfo.getBillingTenor(), Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1,
                                TransBizConstant.ACCOUNT_STATUS_2));
        List<AmInterestAccrualInfo> penaltyInterestAccrualInfos =
                extAmInterestAccrualInfoMapper.selectAccuAcct(amRepaymentPlanInfo.getLoanId(),
                        amRepaymentPlanInfo.getCustomerId(), amRepaymentPlanInfo.getTenantId(),
                        TransBizConstant.INTEREST_ACCRUAL_TYPE_10, amRepaymentPlanInfo.getBillingTenor(),
                        TransBizConstant.INTEREST_ACCRUAL_STATUS_1);
        List<AmInterestAccrualInfo> compoundInterestAccrualInfos =
                extAmInterestAccrualInfoMapper.selectAccuAcct(amRepaymentPlanInfo.getLoanId(),
                        amRepaymentPlanInfo.getCustomerId(), amRepaymentPlanInfo.getTenantId(),
                        TransBizConstant.INTEREST_ACCRUAL_TYPE_20, amRepaymentPlanInfo.getBillingTenor(),
                        TransBizConstant.INTEREST_ACCRUAL_STATUS_1);
        if (!CollectionUtils.isEmpty(amAccountMainInfos)) {
            penaltyAmt = penaltyAmt.add(amAccountMainInfos.stream().filter(e -> TransBizConstant.ACCOUNT_TYPE_11.equals(e.getAccountType()))
                    .map(AmAccountMainInfo::getCurrBalance).reduce(BigDecimal.ZERO, BigDecimal::add));
            compoundAmt = compoundAmt.add(amAccountMainInfos.stream().filter(e -> TransBizConstant.ACCOUNT_TYPE_12.equals(e.getAccountType()))
                    .map(AmAccountMainInfo::getCurrBalance).reduce(BigDecimal.ZERO, BigDecimal::add));
        }
        if (!CollectionUtils.isEmpty(penaltyInterestAccrualInfos)) {
            penaltyAmt = penaltyAmt.add(penaltyInterestAccrualInfos.stream().map(AmInterestAccrualInfo::getAccrualTotalAccruInterest).reduce(BigDecimal.ZERO,
                    BigDecimal::add));
        }
        if (!CollectionUtils.isEmpty(compoundInterestAccrualInfos)) {
            compoundAmt = compoundAmt.add(compoundInterestAccrualInfos.stream().map(AmInterestAccrualInfo::getAccrualTotalAccruInterest).reduce(BigDecimal.ZERO,
                    BigDecimal::add));
        }

        // 实际还款日期
        String transactionDateStr = "";
        if (amRepaymentPlanInfo.getTransactionDate() != null) {
            transactionDateStr = DateUtils.format(amRepaymentPlanInfo.getTransactionDate(), DateUtils.DATE_PATTERN_LINE);
        }

        // 本期结清标志
        int payoffStatus =
                TransBizConstant.LOAN_PLAN_STATUS_7.equals(amRepaymentPlanInfo.getPlanStatus()) || TransBizConstant.LOAN_PLAN_STATUS_8.equals(amRepaymentPlanInfo.getPlanStatus()) ? 0 : 1;

        StringBuilder sb = new StringBuilder();
        sb.append(ReportingConstant.FIELD_PRE_LS).append(amRepaymentPlanInfo.getLoanId()).append(ReportingConstant.SEPARATOR_LS);//贷款业务号
        sb.append(DateUtils.format(amRepaymentPlanInfo.getPaymentDueDate(), DateUtils.DATE_PATTERN_LINE)).append(ReportingConstant.SEPARATOR_LS);
        //应还款日期
        sb.append(graceDateStr).append(ReportingConstant.SEPARATOR_LS); //宽限期最后一天
        sb.append(amRepaymentPlanInfo.getPrincipalAmount() != null ? amRepaymentPlanInfo.getPrincipalAmount() : BigDecimal.ZERO).append(ReportingConstant.SEPARATOR_LS);//应还本金
        sb.append(amRepaymentPlanInfo.getInterestAmount() != null ? amRepaymentPlanInfo.getInterestAmount() : BigDecimal.ZERO).append(ReportingConstant.SEPARATOR_LS);//应还利息
        sb.append(penaltyAmt).append(ReportingConstant.SEPARATOR_LS);//应还本金罚息
        sb.append(compoundAmt).append(ReportingConstant.SEPARATOR_LS);//应还利息罚息
        sb.append(transactionDateStr).append(ReportingConstant.SEPARATOR_LS);//实际还款日期
        sb.append(amRepaymentPlanInfo.getRepayPrincipal() != null ? amRepaymentPlanInfo.getRepayPrincipal() : BigDecimal.ZERO).append(ReportingConstant.SEPARATOR_LS);//实还本金
        sb.append(amRepaymentPlanInfo.getRepayInterest() != null ? amRepaymentPlanInfo.getRepayInterest() : BigDecimal.ZERO).append(ReportingConstant.SEPARATOR_LS);//实还利息
        sb.append(amRepaymentPlanInfo.getRepayPenaltyInterest() != null ? amRepaymentPlanInfo.getRepayPenaltyInterest() : BigDecimal.ZERO).append(ReportingConstant.SEPARATOR_LS);//实还本金罚息
        sb.append(amRepaymentPlanInfo.getRepayCompoundInterest() != null ? amRepaymentPlanInfo.getRepayCompoundInterest() : BigDecimal.ZERO).append(ReportingConstant.SEPARATOR_LS);//实还利息罚息
        sb.append(payoffStatus).append(ReportingConstant.SEPARATOR_LS);//本期结清标志
        sb.append(DateUtils.format(organizationTable.getToday(), DateUtils.DATE_PATTERN_LINE));//导入日期

        return sb.toString();
    }

    /**
     * 生成特殊交易
     *
     * @param tlThirdPartyPayment
     * @return String
     * @author liuyang
     * @date 2021-03-13
     */
    @Override
    public String createLSSpecialTranFileMessage(TlThirdPartyPayment tlThirdPartyPayment) {

        logger.debug("临商特殊交易文件生成，还款流水[{}]", tlThirdPartyPayment.getSeqNo());

        // 变更月数计算
        List<AmRepaymentPlanInfo> amRepaymentPlanInfos =
                extAmRepaymentPlanInfoMapper.selectByLoanIdAndStatus(tlThirdPartyPayment.getLoanId(), Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_7),
                        tlThirdPartyPayment.getCustomerId(), tlThirdPartyPayment.getTenantId());
        int changeMonths = CollectionUtils.isEmpty(amRepaymentPlanInfos) ? 0 : amRepaymentPlanInfos.size();

        StringBuilder sb = new StringBuilder();
        sb.append(ReportingConstant.FIELD_PRE_LS).append(tlThirdPartyPayment.getLoanId()).append(ReportingConstant.SEPARATOR_LS);//贷款业务号
        sb.append(DateUtils.format(tlThirdPartyPayment.getBusinessDate(), DateUtils.DATE_PATTERN_LINE)).append(ReportingConstant.SEPARATOR_LS);
        //发生日期
        sb.append(5).append(ReportingConstant.SEPARATOR_LS);//特殊交易类型 临商只有提前全部还款属于特殊交易
        sb.append(changeMonths).append(ReportingConstant.SEPARATOR_LS);//变更月数 用正、负数、0分别表示延长、缩短、不变
        sb.append(tlThirdPartyPayment.getAmount()).append(ReportingConstant.SEPARATOR_LS);//发生金额
        sb.append("提前全部还款，还款月数变更" + changeMonths + "月,发生金额" + tlThirdPartyPayment.getAmount()).append(ReportingConstant.SEPARATOR_LS);//明细信息
        sb.append("");// 上报标识

        return sb.toString();
    }

    /**
     * 获取客户的详细地址
     *
     * @param customerAddressInfo 客户地址信息
     * @return String
     * @author liuyang
     * @date 2021-03-13
     */
    @Override
    public String getAllAddress(CmCustomerAddressInfo customerAddressInfo) {
        StringBuilder addressSb = new StringBuilder();
        if (!StringUtils.isBlank(customerAddressInfo.getProvince())) {
            PrCodeTable province = codeTableService.findByTypeAndCodeId(ReportingConstant.PROVINCE_TYPE_ID, customerAddressInfo.getProvince(),
                    customerAddressInfo.getTenantId());
            if (null != province) {
                addressSb.append(province.getCodeName());
            }
        }
        if (!StringUtils.isBlank(customerAddressInfo.getCity())) {
            PrCodeTable city = codeTableService.findByTypeAndCodeId(ReportingConstant.CITY_TYPE_ID, customerAddressInfo.getCity(),
                    customerAddressInfo.getTenantId());
            if (null != city) {
                addressSb.append(city.getCodeName());
            }
        }
        if (!StringUtils.isBlank(customerAddressInfo.getDistrict())) {
            PrCodeTable area = codeTableService.findByTypeAndCodeId(ReportingConstant.AREA_TYPE_ID, customerAddressInfo.getDistrict(),
                    customerAddressInfo.getTenantId());
            if (null != area) {
                addressSb.append(area.getCodeName());
            }
        }
        if (!StringUtils.isBlank(customerAddressInfo.getAddress())) {
            addressSb.append(customerAddressInfo.getAddress());
        }
        return addressSb.toString();
    }

    /**
     * 生成对应文件
     *
     * @param messageList
     * @param basePath
     * @param fileName
     * @param fileType
     * @param fileSuffix
     * @param fileCharset
     * @throws IOException
     * @author liuyang
     * @date 2021-03-13
     */
    @Override
    public void generateLSFile(List<String> messageList, String basePath, String fileName, String fileType, String fileSuffix, String fileCharset) throws TxnException {

        // 查询机构信息
        PrOrganizationTable org = organizationTableService.findByOrgIdForBatch(ReportingConstant.DEFAULT_ORG_NUMBER);
        String toDay = DateUtils.format(org.getToday(), DateUtils.DATE_PATTERN_ANOTHER);

        // 文件名
        StringBuilder generateFileNameSb = new StringBuilder();
        generateFileNameSb.append(fileType).append(toDay).append("_").append(fileName).append(fileSuffix);
        String generateFileName = generateFileNameSb.toString();

        // 文件目录
        StringBuilder dirPathSb = new StringBuilder();
        dirPathSb.append(basePath).append(File.separator).append(org.getTenantId()).append(File.separator).append(toDay);
        String dirPath = dirPathSb.toString();

        // 文件全路径
        StringBuilder filePathSb = new StringBuilder();
        filePathSb.append(dirPathSb).append(File.separator).append(generateFileName);
        String filePath = filePathSb.toString();

        // 文件写入
        FileUtils.writeFile(filePath, messageList, fileCharset);
    }

    /**
     * 征信报送码值映射
     *
     * @param codeType 代码类型
     * @param code     代码
     * @return 映射的码值
     * @author liuyang
     * @date 2021-03-13
     */
    @Override
    public String getICRCode(String codeType, String code) {
        Map<String, String> codeDataMap = codeMap.get(codeType);
        String ICRCode = codeDataMap.get(code);
        ICRCode = StringUtils.isNotBlank(ICRCode) ? ICRCode : codeDataMap.get("999999");
        return ICRCode;
    }

    /**
     * 临商文件合并
     *
     * @param sourcePath
     * @param outPath
     * @throws TxnException
     * @author liuyang
     * @date 2021-03-19
     */
    @Override
    public void mergeLSFile(String sourcePath, String outPath) throws TxnException {

        // 查询机构信息
        PrOrganizationTable org = organizationTableService.findByOrgIdForBatch(ReportingConstant.DEFAULT_ORG_NUMBER);
        String toDay = DateUtils.format(org.getToday(), DateUtils.DATE_PATTERN_ANOTHER);

        // 源文件目录
        StringBuilder sourceDirPathSb = new StringBuilder();
        sourceDirPathSb.append(sourcePath).append(File.separator).append(org.getTenantId()).append(File.separator).append(toDay);
        String sourceDirPath = sourceDirPathSb.toString();

        // 源文件列表
        List<String> fileNames = FileUtils.findFileNames(sourceDirPath);
        if (CollectionUtils.isEmpty(fileNames)) {
            logger.error("临商征信文件目录[{}]下没有文件。", sourceDirPath);
            throw new TxnException("临商征信文件目录[" + sourceDirPath + "]下没有文件");
        }

        // 合并文件目录
        StringBuilder outDirPathSb = new StringBuilder();
        outDirPathSb.append(outPath).append(File.separator).append(org.getTenantId()).append(File.separator).append(toDay);
        String outDirPath = outDirPathSb.toString();

        // 账户信息合并
        this.mergeFile(ReportingConstant.PAPH_FILE_TYPE_ACCOUNTINFO, toDay, sourceDirPath, outDirPath, fileNames, ReportingConstant.LS_FILE_SUFFIX);

        // 还款流水合并
        this.mergeFile(ReportingConstant.PAPH_FILE_TYPE_EXTPAYPLAN, toDay, sourceDirPath, outDirPath, fileNames, ReportingConstant.LS_FILE_SUFFIX);

        // 个人信息合并
        this.mergeFile(ReportingConstant.PAPH_FILE_TYPE_INDINFO, toDay, sourceDirPath, outDirPath, fileNames, ReportingConstant.LS_FILE_SUFFIX);

        // 还款计划合并
        this.mergeFile(ReportingConstant.PAPH_FILE_TYPE_LCBACKLIST, toDay, sourceDirPath, outDirPath, fileNames, ReportingConstant.LS_FILE_SUFFIX);

        // 特殊交易合并
        this.mergeFile(ReportingConstant.PAPH_FILE_TYPE_SPECBUSINESS, toDay, sourceDirPath, outDirPath, fileNames, ReportingConstant.LS_FILE_SUFFIX);

        // 生成ok文件
        StringBuilder okFileNameSb = new StringBuilder();
        okFileNameSb.append(ReportingConstant.FILE_PRE_LS).append("_").append(toDay).append(".ok");
        String okFileName = okFileNameSb.toString();
        StringBuilder okFilePathSb = new StringBuilder();
        okFilePathSb.append(outDirPath).append(File.separator).append(okFileName);
        String okFilePath = okFilePathSb.toString();
        FileUtils.createFile(okFilePath);
    }

    /**
     * 文件合并
     *
     * @param fileType      文件类型
     * @param toDay         日期
     * @param sourceDirPath 源文件目录
     * @param outDirPath    合并文件目录
     * @param fileNames     源文件列表
     * @param fileSuffix    文件后缀
     * @throws TxnException
     */
    @Override
    public void mergeFile(String fileType, String toDay, String sourceDirPath, String outDirPath, List<String> fileNames, String fileSuffix) throws TxnException {

        // 合并文件名
        StringBuilder mergeFileNameSb = new StringBuilder();
        mergeFileNameSb.append(fileType).append(toDay).append(fileSuffix);
        String mergeFileName = mergeFileNameSb.toString();

        // 如果合并的文件目录不存在则创建
        if (!FileUtils.isExists(outDirPath)) {
            FileUtils.createDirectories(outDirPath);
        }

        // 合并文件全路径
        StringBuilder mergeFilePathSb = new StringBuilder();
        mergeFilePathSb.append(outDirPath).append(File.separator).append(mergeFileName);
        String mergeFilePath = mergeFilePathSb.toString();
        if (FileUtils.isExists(mergeFilePath)) {
            // 如果合并文件已经存在，则删除掉，支持跑机制
            FileUtils.deleteFile(mergeFilePath);
        }

        // 筛选源文件列表
        String preFileName = mergeFileName.substring(0, mergeFileName.lastIndexOf("."));
        List<String> sourceFileNames =
                fileNames.stream().filter(f -> {
                    if (!f.startsWith(ReportingConstant.FILE_PRE_LS)) {
                        return false;
                    }
                    String pre = f.substring(0, f.lastIndexOf("_"));
                    return pre.equals(preFileName);
                }).collect(Collectors.toList());
        List<String> sourceFilePaths = new ArrayList<>();
        for (String sourceFileName : sourceFileNames) {
            StringBuilder sourceFilePathSb = new StringBuilder();
            sourceFilePathSb.append(sourceDirPath).append(File.separator).append(sourceFileName);
            String sourceFilePath = sourceFilePathSb.toString();
            sourceFilePaths.add(sourceFilePath);
        }
        if (CollectionUtils.isEmpty(sourceFilePaths)) {
            logger.error("合并文件[{}]的源文件不存在。", mergeFilePath);
            throw new TxnException("合并文件[" + mergeFilePath + "]的源文件不存在");
        }

        // 文件合并
        FileUtils.mergeFile(mergeFilePath, sourceFilePaths);
    }

    /**
     * 临商文件传输
     *
     * @param outPath
     * @param ntfsPath
     * @param bizFtypeCode
     * @throws TxnException
     * @author liuyang
     * @date 2021-03-19
     */
    @Override
    public void transLSFile(String outPath, String ntfsPath, String bizFtypeCode) throws TxnException {

        // 查询机构信息
        PrOrganizationTable org = organizationTableService.findByOrgIdForBatch(ReportingConstant.DEFAULT_ORG_NUMBER);
        String toDay = DateUtils.format(org.getToday(), DateUtils.DATE_PATTERN_ANOTHER);

        // 文件目录
        StringBuilder outDirPathSb = new StringBuilder();
        outDirPathSb.append(outPath).append(File.separator).append(org.getTenantId()).append(File.separator).append(toDay);
        String outDirPath = outDirPathSb.toString();

        // 判断临商征信文件是否合并完成
        List<String> fileNames = FileUtils.findFileNames(outDirPath);
        if (CollectionUtils.isEmpty(fileNames)) {
            logger.error("临商征信合并文件目录[{}]下没有文件。", outDirPath);
            throw new TxnException("临商征信合并文件目录[" + outDirPath + "]下没有文件");
        }
        StringBuilder okFileNameSb = new StringBuilder();
        okFileNameSb.append(ReportingConstant.FILE_PRE_LS).append("_").append(toDay).append(".ok");
        String okFileName = okFileNameSb.toString();
        if (!fileNames.contains(okFileName)) {
            logger.error("临商征信文件未完成合并，ok文件[{}]不存在。", okFileName);
            throw new TxnException("临商征信文件未完成合并，ok文件[" + okFileName + "]不存在");
        }

        try {
            // 传输临商征信文件至ntfs
            createTskdFileSampleSimple.initNfsTransFile(outDirPath, ntfsPath, "", bizFtypeCode, org.getTenantId(), toDay, "0");
        } catch (Exception e) {
            logger.error("临商征信文件传输NTFS异常：{}", e);
            throw new TxnException("临商征信文件传输NTFS异常：" + e.getMessage());
        }
    }

    /**
     * 临商，根据字节长度，截取字符串
     *
     * @param str 字符串
     * @param len 字节长度
     * @return
     * @throws TxnBizException
     */
    @Override
    public String getLSLenStr(String str, int len) throws TxnBizException {
        // 如果字符串 Str 为空则直接返回空串
        if (StringUtils.isBlank(str)) {
            return "";
        }

        String charsetName = "GBK"; // 临商字符集：GBK
        char[] chars = str.toCharArray();
        StringBuilder sb = new StringBuilder();
        int useLen = 0;
        for (char c : chars) {
            byte[] bytes;
            try {
                bytes = String.valueOf(c).getBytes(charsetName);
            } catch (UnsupportedEncodingException e) {
                logger.error("不支持字符集[{}]：{}", charsetName, e);
                throw new TxnBizException("不支持字符集：" + charsetName);
            }
            useLen += bytes.length;
            if (useLen > len) {
                break;
            }
            sb.append(c);
        }
        return sb.toString();
    }
}
